package org.example.liuy.service;

import lombok.extern.slf4j.Slf4j;
import org.example.liuy.exception.BusinessException;
import org.example.liuy.service.cache.CacheManager;
import org.example.liuy.service.cache.CachedHighStakes;
import org.example.liuy.service.event.StakeEvent;
import org.example.liuy.service.event.StakeEventListener;
import org.example.liuy.service.event.LoggingBettingEventListener;
import org.example.liuy.service.session.IntelligentSessionManager;
import org.example.liuy.service.session.IntelligentSessionService;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static org.example.liuy.exception.BusinessException.*;

/**
 * 投注管理类 - 使用观察者模式和模板方法模式
 */
@Slf4j
public class StakeManager {

    private static final int BETS_MAP_SEGMENTS = 128; // 增加分段数以提高并发性
    private static final int TOP_N_STAKES = 20; // 每个项目只保留前20名投注
    private static final Long MAX_BET_OFFER_COUNT = 10000000L; // 最大允许的投注项目数量
    private final AtomicInteger betOfferCount = new AtomicInteger(0); // 跟踪投注项目数量

    // 用于存储每个betOfferId的投注数据
    private final Map<Integer, Map<Integer, Double>>[] betsSegments = new ConcurrentHashMap[BETS_MAP_SEGMENTS];
    private final MonitoringService monitoringService;
    private final CacheManager cacheManager;
    private final IntelligentSessionService sessionManager;
    private final List<StakeEventListener> eventListeners = new CopyOnWriteArrayList<>();
    private ScheduledExecutorService cleanupScheduler;

    public StakeManager(MonitoringService monitoringService, CacheManager cacheManager, IntelligentSessionService sessionManager) {
        this.monitoringService = monitoringService;
        this.cacheManager = cacheManager;
        this.sessionManager = sessionManager;
        for (int i = 0; i < BETS_MAP_SEGMENTS; i++) {
            betsSegments[i] = new ConcurrentHashMap<>(16, 0.75f, 4); // 减少初始容量，提高并发度
        }
        registerEventListener(new LoggingBettingEventListener());
    }

    public void registerEventListener(StakeEventListener listener) {
        eventListeners.add(listener);
    }

    /**
     * 提交投注
     * @param betOfferId 投注项目ID
     * @param customerId 客户ID
     * @param stake 投注金额
     * @param sessionKey 会话密钥
     */
    public void submitStake(Integer betOfferId, Integer customerId, double stake, String sessionKey) {
        monitoringService.monitor("betting.submitStake", () -> {
            // 参数验证
            validateParameters(betOfferId, customerId, sessionKey);

            // 会话验证
            validateSession(sessionKey, customerId);

            try {
                int segmentIndex = Math.abs(betOfferId.hashCode()) % BETS_MAP_SEGMENTS;
                Map<Integer, Map<Integer, Double>> betSegment = betsSegments[segmentIndex];

                // 使用computeIfAbsent减少竞争，并跟踪投注项目数量
                Map<Integer, Double> betStakes = betSegment.computeIfAbsent(betOfferId, this::createBetStakesMap);

                double previousStake = betStakes.getOrDefault(customerId, 0.0);
                // 使用merge原子操作更新最大投注金额
                betStakes.merge(customerId, stake, Math::max);

                // 保持只保留前20名投注数据
                maintainTopStakes(betStakes);

                StakeEvent event = new StakeEvent(betOfferId, customerId, stake, previousStake);
                notifyEventListeners(event);

                // 使缓存失效
                cacheManager.remove(betOfferId);

                log.debug("Stake submitted: betOfferId={}, customerId={}, stake={}", betOfferId, customerId, stake);
                return null;
            } catch (Exception e) {
                log.error("Error processing stake for betOfferId: {}, customerId: {}", betOfferId, customerId, e);
                throw e;
            }
        });
    }

    /**
     * 验证参数
     */
    private void validateParameters(Integer betOfferId, Integer customerId, String sessionKey) {
        if (betOfferId == null || customerId == null || sessionKey == null) {
            log.warn("Invalid parameters: betOfferId={}, customerId={}, sessionKey={}",
                    betOfferId, customerId, sessionKey != null ? "present" : "null");
            throw new BusinessException(PARAM_ERROE, "Invalid parameters");
        }
    }

    /**
     * 验证会话
     */
    private void validateSession(String sessionKey, Integer customerId) {
        validateSessionWithRetry(sessionKey, customerId);
    }

    /**
     * 创建投注映射
     */
    private Map<Integer, Double> createBetStakesMap(Integer betOfferId) {
        // 检查投注项目数量是否超过限制
        if (betOfferCount.get() >= MAX_BET_OFFER_COUNT) {
            log.warn("Maximum bet offer count ({}) reached, rejecting new bet offer: {}",
                    MAX_BET_OFFER_COUNT, betOfferId);
            throw new BusinessException(SYSTEM_MAXLIMIT, "Maximum bet offer count reached");
        }

        // 增加投注项目计数
        betOfferCount.incrementAndGet();
        return new ConcurrentHashMap<>(16, 0.75f, 4);
    }

    /**
     * 带重试机制的会话验证
     */
    private void validateSessionWithRetry(String sessionKey, Integer customerId) {
        String msg = "";
        for (int i = 0; i <= 2; i++) {
            try {
                IntelligentSessionManager.SessionValidationResult rs = sessionManager.validateSession(sessionKey, customerId);
                if (rs.isValid()) {
                   return;
                }
                // 如果不是最后一次重试，等待一小段时间后重试
                if (i < 2) {
                    Thread.sleep(10L * (i + 1)); // 递增延迟
                }
                msg = rs.getReason();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                throw new BusinessException(SESSION_ERROE, ie.getMessage());
            } catch (Exception e) {
                log.warn("Session validation exception for customer: {}, attempt: {}", customerId, i + 1, e);
                if (i == 2) {
                    throw new BusinessException(SESSION_ERROE, e.getMessage());
                }
            }
        }
        throw new BusinessException(SESSION_ERROE, msg);
    }

    /**
     * 维护每个投注项目只保留前20名投注数据
     * @param betStakes 投注数据映射
     */
    private void maintainTopStakes(Map<Integer, Double> betStakes) {
        if (betStakes.size() <= TOP_N_STAKES) {
            return; // 如果投注数量不超过20个，无需处理
        }

        // 使用更高效的排序方式
        List<Map.Entry<Integer, Double>> topStakes = betStakes.entrySet()
                .stream()
                .sorted(Map.Entry.<Integer, Double>comparingByValue().reversed())
                .limit(TOP_N_STAKES)
                .toList();

        // 清空原映射并重新填充前20名数据
        betStakes.clear();
        for (Map.Entry<Integer, Double> entry : topStakes) {
            betStakes.put(entry.getKey(), entry.getValue());
        }
    }

    public String getHighStakes(Integer betOfferId) {
        return monitoringService.monitor("betting.getHighStakes", () -> {
            // 空值检查
            if (betOfferId == null) {
                log.debug("getHighStakes called with null betOfferId");
                return "";
            }

            // 尝试从缓存获取
            CachedHighStakes cached = cacheManager.get(betOfferId);
            if (cached != null && cached.isValid()) {
                return cached.getCsvData();
            }

            // 计算最高投注
            List<Map.Entry<Integer, Double>> allStakes = new ArrayList<>();
            for (int i = 0; i < BETS_MAP_SEGMENTS; i++) {
                Map<Integer, Double> betStakes = betsSegments[i].get(betOfferId);
                if (betStakes != null) {
                    allStakes.addAll(betStakes.entrySet());
                }
            }

            // 如果没有投注数据，返回空字符串
            if (allStakes.isEmpty()) {
                return "";
            }

            // 使用普通流进行排序（避免并行流的开销）
            List<Map.Entry<Integer, Double>> sortedStakes = allStakes.stream()
                    .sorted(Map.Entry.<Integer, Double>comparingByValue().reversed())
                    .limit(TOP_N_STAKES)
                    .toList();

            // 构建CSV格式的响应数据
            StringBuilder csvResponse = new StringBuilder();
            for (int i = 0; i < sortedStakes.size(); i++) {
                if (i > 0) {
                    csvResponse.append("\n");
                }
                Map.Entry<Integer, Double> entry = sortedStakes.get(i);
                csvResponse.append(entry.getKey())
                        .append("=")
                        .append(String.format(Locale.US, "%.0f", entry.getValue()));
            }

            String result = csvResponse.toString();

            // 缓存结果
            cacheManager.put(betOfferId, CachedHighStakes.fromCsvData(result, betOfferId));

            return result;
        });
    }

    private void notifyEventListeners(StakeEvent event) {
        for (StakeEventListener listener : eventListeners) {
            try {
                listener.onBettingEvent(event);
            } catch (Exception e) {
                log.error("Error in event listener", e);
            }
        }
    }

    public void startCleanupTask() {
        cleanupScheduler = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r, "BettingCleanupThread");
            t.setDaemon(true);
            return t;
        });
        cleanupScheduler.scheduleAtFixedRate(this::logStatistics, 1, 1, TimeUnit.MINUTES);
    }

    private void logStatistics() {
        monitoringService.monitor("betting.statistics", () -> {
            int totalBets = 0;
            for (int i = 0; i < BETS_MAP_SEGMENTS; i++) {
                for (Map<Integer, Double> bets : betsSegments[i].values()) {
                    totalBets += bets.size();
                }
            }
            log.info("Total active bets: {}", totalBets);
            return null;
        });
    }

    public void stop() {
        if (cleanupScheduler != null) {
            cleanupScheduler.shutdown();
        }
    }
}
